import ezdxf
import pandas as pd
from shapely.geometry import LineString, Point
from scipy.spatial import KDTree
import argparse
import os
import json
import logging
import matplotlib.pyplot as plt
import matplotlib

# 检查依赖
required_packages = ['ezdxf', 'pandas', 'shapely', 'scipy', 'matplotlib']
missing_packages = []
for pkg in required_packages:
    try:
        __import__(pkg)
    except ImportError:
        missing_packages.append(pkg)
if missing_packages:
    print(f"❌ 缺少必要依赖包: {', '.join(missing_packages)}")
    print("请使用以下命令安装: pip install {}".format(' '.join(missing_packages)))
    exit(1)

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)

# 交互式yes/no函数，用于插值和绘图开关
def get_yes_no(prompt_text, default=False):
    prompt = f"{prompt_text} ({'Y/n' if default else 'y/N'}):"
    val = input(prompt).strip().lower()
    if not val:
        return default
    return val in ['y','yes']

# 支持中文显示
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False

# 列出所有图层
def list_layers(msp):
    print("[DEBUG] 列出图层...")
    layers = sorted({e.dxf.layer for e in msp})
    print(f"[DEBUG] 可选图层: {layers}")
    return layers

# 选择图层
def select_layer(name, layers):
    print(f"📌 当前可选 {name} 图层：")
    for i, layer in enumerate(layers, 1):
        print(f" {i}. {layer}")
    while True:
        try:
            idx = int(input(f"请输入{name}图层编号：").strip()) - 1
            if 0 <= idx < len(layers):
                selected = layers[idx]
                print(f"[DEBUG] 选择的{name}图层: {selected}")
                return selected
            else:
                print(f"❌ 无效编号，请输入1到{len(layers)}之间的数字")
        except ValueError:
            print("❌ 输入无效，请输入数字")

# 绘制剖面图
def plot_profile(distances, raw_elevs, offset_elevs, title="航线高程剖面图", save_path=None):
    logger.debug(f"绘图开始, len(distances)={len(distances)}")
    plt.figure()
    plt.plot(distances, raw_elevs, label="原始高程")
    plt.plot(distances, offset_elevs, label="偏移后高程")
    plt.xlabel("距离 (米)")
    plt.ylabel("高程 (米)")
    plt.title(title)
    plt.grid(True)
    plt.legend()
    if save_path:
        plt.savefig(save_path)
        logger.info(f"🖼️ 剖面图已保存为：{save_path}")
    else:
        plt.show()
    plt.close()
    logger.debug("绘图结束")

# 加载/保存 JSON 配置
def load_config(path):
    logger.debug(f"尝试加载配置文件: {path}")
    if os.path.exists(path):
        with open(path, 'r', encoding='utf-8') as f:
            cfg = json.load(f)
            logger.debug(f"配置加载成功: {cfg}")
            return cfg
    logger.debug("未找到配置文件, 使用空配置")
    return {}

def save_config(path, cfg):
    logger.debug(f"保存配置到: {path}")
    with open(path, 'w', encoding='utf-8') as f:
        json.dump(cfg, f, indent=2, ensure_ascii=False)
    logger.info(f"💾 参数已保存至 {path}")

# 主入口
def main():
    print("[DEBUG] 程序开始")
    parser = argparse.ArgumentParser(description="航线高程采样工具")
    parser.add_argument('--file', help='DXF 文件路径')
    parser.add_argument('--route-layer', help='航线图层')
    parser.add_argument('--contour-layer', help='等高线图层')
    parser.add_argument('--step', type=float, help='采样/插值间距（米）')

    parser.add_argument('--plot', action='store_true', help='是否显示图形')
    parser.add_argument('--save-plot', action='store_true', help='是否保存图像')

    parser.add_argument('--z-offset', type=float, help='高程偏移（米）')
    args = parser.parse_args()
    logger.debug(f"CLI 参数: {args}")

    # 获取 DXF 文件路径
    filename = args.file or input("请输入 DXF 文件路径：").strip()
    while not os.path.exists(filename):
        print("❌ 文件不存在，请重新输入。")
        filename = input("请输入 DXF 文件路径：").strip()
    logger.debug(f"使用 DXF 文件: {filename}")

    # JSON 配置文件路径
    cfg_path = os.path.splitext(filename)[0] + '.json'
    cfg = load_config(cfg_path)

    # 确认或重置配置
    if cfg:
        print("📋 检测到配置文件，默认参数如下：")
        for k, v in cfg.items(): print(f"  {k}: {v}")
        if input("使用这些参数？(Y/n):").strip().lower() == 'n':
            logger.debug("用户选择重置配置")
            cfg = {}
    else:
        logger.warning("⚠️ 未检测到配置文件，将使用交互模式获取参数。")

    # 参数获取
    step = args.step if args.step is not None else cfg.get('step', 1.0)
    z_offset = args.z_offset if args.z_offset is not None else cfg.get('z_offset', 0.0)
    output_dir = os.path.dirname(filename)
    plot_fig = args.plot if args.plot is not None else cfg.get('plot', False)
    save_plot = args.save_plot or cfg.get('save_plot', False)
    generate_kml = cfg.get('generate_kml', False)
    transform_coords = cfg.get('transform_coords', False)
    src_epsg = cfg.get('src_epsg', 4548)  # 默认CGCS2000
    dest_epsg = cfg.get('dest_epsg', 4326)  # 默认WGS84
    
    # 读取DXF文件并获取图层
    try:
        doc = ezdxf.readfile(filename)
        msp = doc.modelspace()
        logger.info(f"成功读取DXF文件: {filename}")
    except ezdxf.DXFError as e:
        logger.error(f"DXF文件读取错误: {str(e)}")
        exit(1)
    except Exception as e:
        logger.error(f"文件处理异常: {str(e)}")
        exit(1)
    
    layers = list_layers(msp)
    
    # 检查是否需要用户确认所有参数（没有配置文件或用户选择不使用配置文件）
    need_confirm_all = not cfg or (cfg and input("使用这些参数？(Y/n):").strip().lower() == 'n')
    
    # 只有在不需要完全确认或有配置时才预先选择图层
    if not need_confirm_all:
        route_layer = args.route_layer or cfg.get('route_layer') or select_layer('航线', layers)
        contour_layer = args.contour_layer or cfg.get('contour_layer') or select_layer('等高线', layers)
    else:
        # 如果需要完全确认，则初始化为空值，稍后让用户选择
        route_layer = args.route_layer or cfg.get('route_layer', '')
        contour_layer = args.contour_layer or cfg.get('contour_layer', '')
    
    # 参数确认
    print("\n📝 当前参数汇总:")
    print(f"  航线图层: {route_layer if route_layer else '未选择'}")
    print(f"  等高线图层: {contour_layer if contour_layer else '未选择'}")
    print(f"  采样间距: {step} 米")
    print(f"  高程偏移: {z_offset} 米")
    print(f"  输出目录: {output_dir}")
    print(f"  显示图形: {'是' if plot_fig else '否'}")
    print(f"  保存图形: {'是' if save_plot else '否'}")
    
    if need_confirm_all:
        print("\n🔧 开始修改参数...")
        # 直接让用户重新选择图层
        route_layer = select_layer('航线', layers)
        contour_layer = select_layer('等高线', layers)
        
        # 其他参数修改
        step = float(input(f"采样间距(米) [{step}]: ") or step)
        z_offset = float(input(f"高程偏移(米) [{z_offset}]: ") or z_offset)
        output_dir = input(f"输出目录 [{output_dir}]: ") or output_dir
        plot_fig = get_yes_no("是否显示图形", plot_fig)
        save_plot = get_yes_no("是否保存图形", save_plot)
        
        # KML生成和坐标转换配置
        generate_kml = get_yes_no("是否生成KML文件?", cfg.get('generate_kml', True))
        transform_coords = True
        src_epsg = cfg.get('src_epsg', 4548)  # 默认CGCS2000
        dest_epsg = cfg.get('dest_epsg', 4326)  # 默认WGS84
        
        if generate_kml:
            transform_coords = get_yes_no("是否进行坐标转换?", cfg.get('transform_coords', False))
            if transform_coords:
                print("\n📌 坐标系统配置:")
                print(f"  当前源坐标系: EPSG:{src_epsg}")
                print(f"  当前目标坐标系: EPSG:{dest_epsg}")
                
                if not get_yes_no("使用当前坐标系配置?", True):
                    try:
                        src_input = input(f"请输入源坐标系EPSG代码 [{src_epsg}]: ").strip()
                        dest_input = input(f"请输入目标坐标系EPSG代码 [{dest_epsg}]: ").strip()
                        
                        src_epsg = int(src_input) if src_input else src_epsg
                        dest_epsg = int(dest_input) if dest_input else dest_epsg
                    except ValueError:
                        logger.error("无效的EPSG代码，将不进行坐标转换")
                        transform_coords = False
        
        # 最终确认
        print("\n📝 修改后的参数:")
        print(f"  航线图层: {route_layer}")
        print(f"  等高线图层: {contour_layer}")
        print(f"  采样间距: {step} 米")
        print(f"  高程偏移: {z_offset} 米")
        print(f"  输出目录: {output_dir}")
        print(f"  显示图形: {'是' if plot_fig else '否'}")
        print(f"  保存图形: {'是' if save_plot else '否'}")
        if generate_kml:
            print(f"  生成KML文件: 是")
            if transform_coords:
                print(f"  坐标转换: 是 (EPSG:{src_epsg} → EPSG:{dest_epsg})")
            else:
                print(f"  坐标转换: 否")
        else:
            print(f"  生成KML文件: 否")
        
        if not get_yes_no("确认使用修改后的参数?", True):
            logger.error("用户取消操作")
            exit(1)
    elif not route_layer or not contour_layer:
        # 如果不是完全确认但图层未设置，则需要选择图层
        route_layer = route_layer or select_layer('航线', layers)
        contour_layer = contour_layer or select_layer('等高线', layers)
        # 再次确认参数
        if not get_yes_no("这些参数是否正确?", True):
            print("\n🔧 开始修改参数...")
            route_layer = select_layer('航线', layers)
            contour_layer = select_layer('等高线', layers)
            step = float(input(f"采样间距(米) [{step}]: ") or step)
            z_offset = float(input(f"高程偏移(米) [{z_offset}]: ") or z_offset)
            output_dir = input(f"输出目录 [{output_dir}]: ") or output_dir
            plot_fig = get_yes_no("是否显示图形", plot_fig)
            save_plot = get_yes_no("是否保存图形", save_plot)
        
        # 其他参数修改
        step = float(input(f"采样间距(米) [{step}]: ") or step)
        z_offset = float(input(f"高程偏移(米) [{z_offset}]: ") or z_offset)
        output_dir = input(f"输出目录 [{output_dir}]: ") or output_dir
        plot_fig = get_yes_no("是否显示图形", plot_fig)
        save_plot = get_yes_no("是否保存图形", save_plot)
        
        # KML生成和坐标转换配置
        generate_kml = get_yes_no("是否生成KML文件?", cfg.get('generate_kml', True))
        transform_coords = True
        src_epsg = cfg.get('src_epsg', 4548)  # 默认CGCS2000
        dest_epsg = cfg.get('dest_epsg', 4326)  # 默认WGS84
        
        if generate_kml:
            transform_coords = get_yes_no("是否进行坐标转换?", cfg.get('transform_coords', False))
            if transform_coords:
                print("\n📌 坐标系统配置:")
                print(f"  当前源坐标系: EPSG:{src_epsg}")
                print(f"  当前目标坐标系: EPSG:{dest_epsg}")
                
                if not get_yes_no("使用当前坐标系配置?", True):
                    try:
                        src_input = input(f"请输入源坐标系EPSG代码 [{src_epsg}]: ").strip()
                        dest_input = input(f"请输入目标坐标系EPSG代码 [{dest_epsg}]: ").strip()
                        
                        src_epsg = int(src_input) if src_input else src_epsg
                        dest_epsg = int(dest_input) if dest_input else dest_epsg
                    except ValueError:
                        logger.error("无效的EPSG代码，将不进行坐标转换")
                        transform_coords = False
        
        # 最终确认
        print("\n📝 修改后的参数:")
        print(f"  航线图层: {route_layer}")
        print(f"  等高线图层: {contour_layer}")
        print(f"  采样间距: {step} 米")
        print(f"  高程偏移: {z_offset} 米")
        print(f"  输出目录: {output_dir}")
        print(f"  显示图形: {'是' if plot_fig else '否'}")
        print(f"  保存图形: {'是' if save_plot else '否'}")
        if generate_kml:
            print(f"  生成KML文件: 是")
            if transform_coords:
                print(f"  坐标转换: 是 (EPSG:{src_epsg} → EPSG:{dest_epsg})")
            else:
                print(f"  坐标转换: 否")
        else:
            print(f"  生成KML文件: 否")
        
        if not get_yes_no("确认使用修改后的参数?", True):
            logger.error("用户取消操作")
            exit(1)
    
    # 保存更新配置
    new_cfg = {
        'route_layer': route_layer,
        'contour_layer': contour_layer,
        'step': step,
        'z_offset': z_offset,
        'plot': plot_fig,
        'save_plot': save_plot,
        'generate_kml': generate_kml,
        'transform_coords': transform_coords,
        'src_epsg': src_epsg,
        'dest_epsg': dest_epsg
    }
    save_config(cfg_path, new_cfg)

    os.makedirs(output_dir, exist_ok=True)

    # 提取等高线 Z 并构建空间索引
    contour_points = []
    contour_zs = []
    for e in msp.query(f"LWPOLYLINE[layer=='{contour_layer}']"):
        z = e.dxf.elevation if e.dxf.hasattr('elevation') else 0
        coords = [(pt[0], pt[1]) for pt in e]
        for (x, y) in coords:
            contour_points.append((x, y))
            contour_zs.append(z)
    tree = KDTree(contour_points) if contour_points else None

    # 处理航线
    polylines = [e for e in msp.query(f"LWPOLYLINE[layer=='{route_layer}']") if len(e) >= 2]
    print(f"✈️ 共检测到 {len(polylines)} 条航线")

    for idx, e in enumerate(polylines, 1):
        coords = [(pt[0], pt[1]) for pt in e]
        route = LineString(coords)
        num_points = int(route.length / step) + 1
        distances = [i * step for i in range(num_points)]
        data = []
        raw_elevs = []
        off_elevs = []

        for d in distances:
            pt = route.interpolate(d)
            px, py = pt.x, pt.y
            elevation = 0.0
            if contour_points:
                if tree and len(contour_points) >= 3:
                    dists, indices = tree.query((px, py), k=3)
                    dists = [d if d > 0 else 1e-6 for d in dists]
                    weights = [1/(d**2) for d in dists]
                    total_weight = sum(weights)
                    elevation = sum(w * contour_zs[i] for w, i in zip(weights, indices)) / total_weight
                elif tree and len(contour_points) == 1:
                    elevation = contour_zs[0]
                else:
                    elevation = 0.0
            raw_elevs.append(elevation)
            off_elevs.append(elevation + z_offset)
            data.append((d, px, py, round(elevation,2), round(elevation+z_offset,2)))

        df = pd.DataFrame(data, columns=['Distance','X','Y','RawElevation','OffsetElevation'])
        out_csv = os.path.join(output_dir, f"line_{idx:03}.csv")
        df.to_csv(out_csv, index=False)
        print(f"✅ 航线{idx} CSV已保存：{out_csv}")

        if plot_fig or save_plot:
            img_path = os.path.join(output_dir, f"line_{idx:03}.png") if save_plot else None
            plot_profile(distances, raw_elevs, off_elevs, title=f"航线{idx}剖面", save_path=img_path)

    # 处理KML生成和坐标转换
    if generate_kml:
        from src.coordinate_converter import CoordinateConverter
        converter = CoordinateConverter()
        
        for idx in range(1, len(polylines) + 1):
            csv_path = os.path.join(output_dir, f"line_{idx:03}.csv")
            if not os.path.exists(csv_path):
                logger.warning(f"CSV文件不存在: {csv_path}")
                continue
            
            # 坐标转换
            transformed_csv = None
            if transform_coords:
                transformed_csv = converter.transform_coordinates(csv_path, src_epsg, dest_epsg)
                if not transformed_csv:
                    logger.error(f"航线{idx}坐标转换失败")
                    continue
            
            # KML生成
            kml_path = os.path.join(output_dir, f"line_{idx:03}.kml")
            success = converter.generate_kml(
                transformed_csv if transform_coords else csv_path,
                kml_path
            )
            
            if success:
                logger.info(f"✅ 航线{idx} KML文件已生成: {kml_path}")

    logger.info("程序结束")

if __name__ == '__main__':
    main()